package com.zyw.codegen.java;

import com.zyw.codegen.java.elements.AbstractMVCMethodGenerator;
import com.zyw.orm.support.page.Page;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.dom.java.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

/**
 * @author zhangyw
 * @date 2017/3/14 14:15
 */
public class ServiceGenerator extends MVCGenerator {

    public ServiceGenerator() {
    }

    @Override
    public List<CompilationUnit> getCompilationUnits() {
        progressCallback.startTask(MessageFormat.format("Generating Service for table {0}", //$NON-NLS-1$
                introspectedTable.getFullyQualifiedTable().toString()));

        TopLevelClass topLevelClass = getTopLevelClass();

        IntrospectedColumn primaryColumn = introspectedTable.getPrimaryKeyColumns().get(0);

        addGetBeanMethod(topLevelClass, primaryColumn);
        addInsertBeanMethod(topLevelClass);
        addUpdateBeanMethod(topLevelClass);
        addDeleteBeanMethod(topLevelClass,primaryColumn);
        addFindByPageMethod(topLevelClass);
        List<CompilationUnit> answer = new ArrayList<CompilationUnit>();
        answer.add(topLevelClass);

        return answer;
    }

    private TopLevelClass getTopLevelClass() {
        TopLevelClass topLevelClass = new TopLevelClass(new FullyQualifiedJavaType(getServiceClassName()));
        topLevelClass.setVisibility(JavaVisibility.PUBLIC);
        context.getCommentGenerator().addJavaFileComment(topLevelClass);

        FullyQualifiedJavaType superClass = new FullyQualifiedJavaType(getRootClass());
        // 添加实体泛型
        superClass.addTypeArgument(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));
        // 添加主键泛型
        superClass.addTypeArgument(introspectedTable.getPrimaryKeyColumns().get(0).getFullyQualifiedJavaType());
        FullyQualifiedJavaType daoInterfaceType = new FullyQualifiedJavaType(introspectedTable.getMyBatis3JavaMapperType());
        // 添加DAO泛型
        superClass.addTypeArgument(daoInterfaceType);

        topLevelClass.setSuperClass(superClass);
        topLevelClass.addImportedType(new FullyQualifiedJavaType(getRootClass()));
        topLevelClass.addImportedType(new FullyQualifiedJavaType(introspectedTable.getMyBatis3JavaMapperType()));
        topLevelClass.addImportedType(new FullyQualifiedJavaType(Service.class.getName()));
        topLevelClass.addAnnotation("@Service");

        return topLevelClass;
    }

    @SuppressWarnings("rawtypes")
	private void addGetBeanMethod(TopLevelClass topLevelClass, final IntrospectedColumn primaryColumn) {
        AbstractMVCMethodGenerator methodGenerator = new AbstractMVCMethodGenerator<TopLevelClass>() {
            @Override
            public void addJavaElements(TopLevelClass topLevelClass) {
                FullyQualifiedJavaType recordType =
                        new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

                FullyQualifiedJavaType paramType = primaryColumn.getFullyQualifiedJavaType();

                Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
                importedTypes.add(recordType);
                importedTypes.add(paramType);

                Method method = new Method();
                method.setVisibility(JavaVisibility.PUBLIC);
                method.setName("get");
                String paramName = primaryColumn.getJavaProperty();
                method.addParameter(new Parameter(paramType, paramName));
                method.setReturnType(recordType);
                method.addBodyLine("return getDao().selectByPrimaryKey("+ paramName + ");"); //$NON-NLS-1$

                context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

                topLevelClass.addImportedTypes(importedTypes);
                topLevelClass.addMethod(method);
            }
        };

        initializeAndExecuteGenerator(methodGenerator, topLevelClass);
    }

    @SuppressWarnings("rawtypes")
    private void addInsertBeanMethod(TopLevelClass topLevelClass) {
        AbstractMVCMethodGenerator methodGenerator = new AbstractMVCMethodGenerator<TopLevelClass>() {
            @Override
            public void addJavaElements(TopLevelClass topLevelClass) {
                FullyQualifiedJavaType returnType =
                        new FullyQualifiedJavaType(int.class.getName());

                FullyQualifiedJavaType paramType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

                Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
                importedTypes.add(returnType);
                importedTypes.add(paramType);

                Method method = new Method();
                method.setVisibility(JavaVisibility.PUBLIC);
                method.setName("insert");
                method.addParameter(new Parameter(paramType, "record"));
                method.setReturnType(returnType);
                method.addBodyLine("return getDao().insert(record);");
                method.addAnnotation("@Transactional");
                importedTypes.add(new FullyQualifiedJavaType(Transactional.class.getName()));

                context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

                topLevelClass.addImportedTypes(importedTypes);
                topLevelClass.addMethod(method);
            }
        };

        initializeAndExecuteGenerator(methodGenerator, topLevelClass);
    }

    @SuppressWarnings("rawtypes")
    private void addUpdateBeanMethod(TopLevelClass topLevelClass) {
        AbstractMVCMethodGenerator methodGenerator = new AbstractMVCMethodGenerator<TopLevelClass>() {
            @Override
            public void addJavaElements(TopLevelClass topLevelClass) {
                FullyQualifiedJavaType returnType =
                        new FullyQualifiedJavaType(int.class.getName());

                FullyQualifiedJavaType paramType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

                Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
                importedTypes.add(returnType);
                importedTypes.add(paramType);

                Method method = new Method();
                method.setVisibility(JavaVisibility.PUBLIC);
                method.setName("update");
                method.addParameter(new Parameter(paramType, "record"));
                method.setReturnType(returnType);
                method.addBodyLine("return getDao().updateByPrimaryKey(record);");
                method.addAnnotation("@Transactional");
                importedTypes.add(new FullyQualifiedJavaType(Transactional.class.getName()));

                context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

                topLevelClass.addImportedTypes(importedTypes);
                topLevelClass.addMethod(method);
            }
        };

        initializeAndExecuteGenerator(methodGenerator, topLevelClass);
    }

    @SuppressWarnings("rawtypes")
    private void addDeleteBeanMethod(TopLevelClass topLevelClass, final IntrospectedColumn primaryColumn) {
        AbstractMVCMethodGenerator methodGenerator = new AbstractMVCMethodGenerator<TopLevelClass>() {
            @Override
            public void addJavaElements(TopLevelClass topLevelClass) {
                FullyQualifiedJavaType returnType =
                        new FullyQualifiedJavaType(int.class.getName());

                FullyQualifiedJavaType paramType = primaryColumn.getFullyQualifiedJavaType();

                Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
                importedTypes.add(returnType);
                importedTypes.add(paramType);

                Method method = new Method();
                method.setVisibility(JavaVisibility.PUBLIC);
                method.setName("delete");
                String paramName = primaryColumn.getJavaProperty();
                method.addParameter(new Parameter(paramType, paramName));
                method.setReturnType(returnType);
                method.addBodyLine("return getDao().deleteByPrimaryKey(" + paramName + ");");
                method.addAnnotation("@Transactional");
                importedTypes.add(new FullyQualifiedJavaType(Transactional.class.getName()));

                context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

                topLevelClass.addImportedTypes(importedTypes);
                topLevelClass.addMethod(method);
            }
        };

        initializeAndExecuteGenerator(methodGenerator, topLevelClass);
    }

    @SuppressWarnings("rawtypes")
    private void addFindByPageMethod(TopLevelClass topLevelClass) {
        AbstractMVCMethodGenerator methodGenerator = new AbstractMVCMethodGenerator<TopLevelClass>() {
            @Override
            public void addJavaElements(TopLevelClass topLevelClass) {
                FullyQualifiedJavaType returnType = FullyQualifiedJavaType.getNewListInstance();
                returnType.addTypeArgument(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));

                FullyQualifiedJavaType paramType = new FullyQualifiedJavaType(Page.class.getName());
                paramType.addTypeArgument(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));

                Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
                importedTypes.add(FullyQualifiedJavaType.getNewListInstance());
                importedTypes.add(new FullyQualifiedJavaType(Page.class.getName()));

                Method method = new Method();
                method.setVisibility(JavaVisibility.PUBLIC);
                method.setName("findByPage");
                method.addParameter(new Parameter(paramType, "page"));
                method.setReturnType(returnType);

                StringBuffer sb = new StringBuffer();
                sb.append(returnType.getShortName());
                sb.append(" list = ");
                sb.append("getDao().findByPage(page);");
                method.addBodyLine(sb.toString());
                method.addBodyLine("page.setList(list);");
                method.addBodyLine("return list;");

                context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

                topLevelClass.addImportedTypes(importedTypes);
                topLevelClass.addMethod(method);
            }
        };

        initializeAndExecuteGenerator(methodGenerator, topLevelClass);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private void initializeAndExecuteGenerator(AbstractMVCMethodGenerator methodGenerator, JavaElement javaElement) {
        methodGenerator.setContext(context);
        methodGenerator.setIntrospectedTable(introspectedTable);
        methodGenerator.setProgressCallback(progressCallback);
        methodGenerator.setWarnings(warnings);
        methodGenerator.addJavaElements(javaElement);
    }
}
